package com.zhy.generator.config;

import com.zhy.generator.codegen.api.*;
import com.zhy.generator.codegen.internal.ObjectFactory;
import com.zhy.generator.codegen.internal.PluginAggregator;
import com.zhy.generator.codegen.internal.db.DatabaseIntrospector;

import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static com.zhy.generator.codegen.util.Messages.getString;

public class Context {

    private TableConfiguration tableConfiguration;

    private ConfigurationBuilder configurationBuilder;

    private List<PluginConfiguration> pluginConfigurations;

    private Boolean enableSubPackages;

    private PluginAggregator pluginAggregator;

    private CommentGenerator commentGenerator;

    private JavaTypeResolverConfiguration javaTypeResolverConfiguration;

    private boolean trimStrings;

    private Boolean autoDelimitKeywords;

    private List<IntrospectedTable> introspectedTables;

    private JavaFormatter javaFormatter;

    private XmlFormatter xmlFormatter;

    private DatabaseMetaData metaData;

    public Context(){
        this.initPlugins();
        introspectedTables = new ArrayList<>();
    }

    public TableConfiguration getTableConfiguration() {
        return tableConfiguration;
    }

    public void setTableConfiguration(TableConfiguration tableConfiguration) {
        this.tableConfiguration = tableConfiguration;
    }

    public Boolean getEnableSubPackages() {
        return enableSubPackages;
    }

    public void setEnableSubPackages(Boolean enableSubPackages) {
        this.enableSubPackages = enableSubPackages;
    }

    public JavaTypeResolverConfiguration getJavaTypeResolverConfiguration() {
        return javaTypeResolverConfiguration;
    }

    public void setJavaTypeResolverConfiguration(JavaTypeResolverConfiguration javaTypeResolverConfiguration) {
        this.javaTypeResolverConfiguration = javaTypeResolverConfiguration;
    }

    public boolean isTrimStrings() {
        return trimStrings;
    }

    public void setTrimStrings(boolean trimStrings) {
        this.trimStrings = trimStrings;
    }

    public ConfigurationBuilder getConfigurationBuilder() {
        return configurationBuilder;
    }

    public void setConfigurationBuilder(ConfigurationBuilder configurationBuilder) {
        this.configurationBuilder = configurationBuilder;
    }

    public DatabaseMetaData getMetaData() {
        return metaData;
    }

    public void setMetaData(DatabaseMetaData metaData) {
        this.metaData = metaData;
    }

    public void setIntrospectedTables(List<IntrospectedTable> introspectedTables) {
        this.introspectedTables = introspectedTables;
    }

    public Plugin getPlugins() {
        return pluginAggregator;
    }

    public void addPluginConfiguration(PluginConfiguration pluginConfiguration) {
        pluginConfigurations.add(pluginConfiguration);
    }


    public CommentGenerator getCommentGenerator() {
        if (commentGenerator == null) {
            commentGenerator = ObjectFactory.createCommentGenerator(this);
        }
        return commentGenerator;
    }

    public void initPlugins(){
        pluginConfigurations = new ArrayList<>();
        PluginConfiguration pluginConfiguration = new PluginConfiguration();
        pluginConfiguration.setConfigurationType("com.zhy.generator.codegen.mybatis.plugin.LombokPlugin");
        pluginConfiguration.addProperty("hasLombok","true");
        this.addPluginConfiguration(pluginConfiguration);
    }

    public Configuration getConfiguration(String field) {
        Configuration config = null;
        if(ConfigurationConstant.CONFIGURATION_CONSTANT.equals(field)){
            config = this.configurationBuilder.constantConfiguration();
        }else if(ConfigurationConstant.CONFIGURATION_CONTROLLER.equals(field)){
            config = this.configurationBuilder.controllerConfiguration();
        }else if(ConfigurationConstant.CONFIGURATION_REQUEST.equals(field)){
            config = this.configurationBuilder.requestConfiguration();
        }else if(ConfigurationConstant.CONFIGURATION_HELPER.equals(field)){
            config = this.configurationBuilder.helperConfiguration();
        }else if(ConfigurationConstant.CONFIGURATION_PO.equals(field)){
            config = this.configurationBuilder.poConfiguration();
        }else if(ConfigurationConstant.CONFIGURATION_VO.equals(field)){
            config = this.configurationBuilder.voConfiguration();
        }else if(ConfigurationConstant.CONFIGURATION_SERVICE.equals(field)){
            config = this.configurationBuilder.serviceConfiguration();
        }else if(ConfigurationConstant.CONFIGURATION_MAPPER.equals(field)){
            config = this.configurationBuilder.mapperConfiguration();
        }else if(ConfigurationConstant.CONFIGURATION_SQLMAP.equals(field)){
            config = this.configurationBuilder.sqlmapConfiguration();
        }
        return config;
    }

    public int getIntrospectionSteps() {
        int steps = 0;

        steps++; // connect to database

        steps += introspectedTables.size() * 1;

        return steps;
    }

    public int getGenerationSteps() {
        int steps = 0;

        if (introspectedTables != null) {
            for (IntrospectedTable introspectedTable : introspectedTables) {
                steps += introspectedTable.getGenerationSteps();
            }
        }

        return steps;
    }

    public JavaFormatter getJavaFormatter() {
        if (javaFormatter == null) {
            javaFormatter = ObjectFactory.createJavaFormatter(this);
        }
        return javaFormatter;
    }

    public XmlFormatter getXmlFormatter() {
        if (xmlFormatter == null) {
            xmlFormatter = ObjectFactory.createXmlFormatter(this);
        }
        return xmlFormatter;
    }

    public boolean autoDelimitKeywords() {
        return autoDelimitKeywords != null && autoDelimitKeywords.booleanValue();
    }

    public void generateFiles(ProgressCallback callback, List<GeneratedJavaFile> generatedJavaFiles, List<GeneratedXmlFile> generatedXmlFiles, List<String> warnings) throws InterruptedException {

        pluginAggregator = new PluginAggregator();
        for (PluginConfiguration pluginConfiguration : pluginConfigurations) {
            Plugin plugin = ObjectFactory.createPlugin(this,pluginConfiguration);
            if (plugin.validate(warnings)) {
                pluginAggregator.addPlugin(plugin);
            } else {
                warnings.add(getString("Warning.24", pluginConfiguration.getConfigurationType()));
            }
        }

        if (introspectedTables != null) {
            for (IntrospectedTable introspectedTable : introspectedTables) {
                callback.checkCancel();
                introspectedTable.initialize();
                introspectedTable.calculateGenerators(warnings, callback);
                generatedJavaFiles.addAll(introspectedTable.getGeneratedJavaFiles());
                generatedXmlFiles.addAll(introspectedTable.getGeneratedXmlFiles());
                generatedJavaFiles.addAll(pluginAggregator.contextGenerateAdditionalJavaFiles(introspectedTable));
                generatedXmlFiles.addAll(pluginAggregator.contextGenerateAdditionalXmlFiles(introspectedTable));
            }
        }

        generatedJavaFiles.addAll(pluginAggregator.contextGenerateAdditionalJavaFiles());
        generatedXmlFiles.addAll(pluginAggregator.contextGenerateAdditionalXmlFiles());
    }


    public void introspectTables(ProgressCallback callback, List<String> warnings, Set<String> fullyQualifiedTableNames) throws InterruptedException, SQLException {
        String catalog = this.tableConfiguration.getCatalog();
        String schema = this.tableConfiguration.getSchema();
        JavaTypeResolver javaTypeResolver = ObjectFactory.createJavaTypeResolver(this, warnings);

        DatabaseIntrospector databaseIntrospector = new DatabaseIntrospector(this, javaTypeResolver, warnings, metaData);
        callback.startTask(getString("Progress.0"));

        List<String> tableNames = databaseIntrospector.calculateTables(catalog, schema,null);
        if (!this.tableConfiguration.areAnyStatementsEnabled()) {
            warnings.add(getString("Warning.0", "")); //$NON-NLS-1$
            return;
        }
        for (String tableName : tableNames) {
            callback.startTask(getString("Progress.1", tableName)); //$NON-NLS-1$
            List<IntrospectedTable> tables = databaseIntrospector.introspectTables(tableName);
            if (tables != null) {
                introspectedTables.addAll(tables);
            }
            callback.checkCancel();
        }
    }


}
